/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.core.ui.dialogs;


import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EAttribute;
import org.eclipse.emf.ecore.EEnum;
import org.eclipse.emf.ecore.EEnumLiteral;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.Stereotype;

import com.cea.papyrus.core.PapyrusPlugin;



/**
 * 
 */
public class CompositeClassPropertiesDialog extends Dialog {

	/**
	 * 
	 */
	private EObject compositeClass;
	
	/**
	 * 
	 */
	private SelectionListener propListener = null;
	
	/**
	 * 
	 */
	private Table properties, propValuesTable;
	
	/**
	 * 
	 */
	private Button addButton, removeButton, upProp, downProp; 
	
	/**
	 * 
	 */
	private TableColumn valuesColumn;
	
	/**
	 * 
	 */
	private Listener doubleClickListener=null;
	
	/**
	 * 
	 */
	private static final Image IMG_ADD		= PapyrusPlugin.getImage(PapyrusPlugin.PAPYRUS_ICONS_16x16+"Add_16x16.gif");
	
	/**
	 * 
	 */
	private static final Image IMG_DELETE	= PapyrusPlugin.getImage(PapyrusPlugin.PAPYRUS_ICONS_16x16+"Delete_16x16.gif");
	
	/**
	 * 
	 */
	private static final Image IMG_UP		= PapyrusPlugin.getImage(PapyrusPlugin.PAPYRUS_ICONS_16x16+"ArrowUp_16x16.gif");
	
	/**
	 * 
	 */
	private static final Image IMG_DOWN		= PapyrusPlugin.getImage(PapyrusPlugin.PAPYRUS_ICONS_16x16+"ArrowDown_16x16.gif");

	
	/**
	 * 
	 * 
	 * @param parentShell 
	 * @param compClass 
	 * @param st 
	 * @param umlElt 
	 */
	public CompositeClassPropertiesDialog(Shell parentShell, EObject compClass, Element umlElt, Stereotype st) {
		super(parentShell);
		this.compositeClass = compClass;
	}
	

    /* (non-Javadoc)
     * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite)
     */
    /**
     * 
     * 
     * @param parent 
     * 
     * @return 
     */
    @Override
	protected Control createDialogArea(Composite parent) {
    	Composite comp=(Composite)super.createDialogArea(parent);
    	    	
		GridLayout layout=(GridLayout)comp.getLayout();
    	//GridLayout layout = new GridLayout();
    	
		layout.numColumns=7;
		layout.horizontalSpacing=10;
		layout.marginHeight = 10;
		layout.marginWidth = 10;
		
		Shell shell = comp.getShell();
		shell.setLayout(layout);
		
		properties = new Table(shell, SWT.SINGLE | SWT.V_SCROLL | SWT.BORDER | SWT.FULL_SELECTION);
		TableColumn propColumn = new TableColumn(properties, SWT.LEFT);
		propColumn.setWidth(250);
		properties.setHeaderVisible(true);
		propColumn.setText(compositeClass.eClass().getName());
		
		properties.setLayoutData(new GridData(0, SWT.BEGINNING, false, true));
		
		refreshProperties();
		
		/*EList props = compositeClass.eClass().getEAllStructuralFeatures();
		
		Iterator iter = props.iterator();
		
		while(iter.hasNext()){
			EStructuralFeature prop = (EStructuralFeature)iter.next();
			TableItem item = new TableItem(properties, SWT.NONE);

			if(prop.getUpperBound()==-1)
				item.setText(prop.getName()
						+" ["+prop.getLowerBound()+"..*] = "
						+ compositeClass.eGet(prop));

			else
				item.setText(prop.getName()
					+" ["+prop.getLowerBound()+".."+prop.getUpperBound()+"] = "
					+ compositeClass.eGet(prop));
			
			item.setData(prop);
						
			propListener = new SelectionListener() {
				public void widgetSelected(SelectionEvent e) {
					propertiesListener();
				}
				
				public void widgetDefaultSelected(SelectionEvent e){
				}
			};
			properties.addSelectionListener(propListener);
		}*/
		
		propValuesTable = new Table(shell, SWT.SINGLE | SWT.V_SCROLL | SWT.BORDER | SWT.FULL_SELECTION);
		valuesColumn = new TableColumn(propValuesTable, SWT.LEFT);
		valuesColumn.setWidth(250);
		propValuesTable.setHeaderVisible(true);

		propValuesTable.setLayoutData(new GridData(0, SWT.BEGINNING, false, true));
		
		addButton = new Button(shell, SWT.PUSH);
		addButton.setImage(IMG_ADD);
		addButton.setVisible(true);
		
		addButton.setLayoutData(new GridData(0, SWT.BEGINNING, false, false));
		
		addButton.addMouseListener(new MouseListener() {
			public void mouseDoubleClick(MouseEvent e) {
			}

			public void mouseDown(MouseEvent e) {
			}

			public void mouseUp(MouseEvent e) {
				addButtonListener();
			}
		});

		removeButton = new Button(shell, SWT.PUSH);
		removeButton.setImage(IMG_DELETE);
		removeButton.setVisible(true);
		
		removeButton.setLayoutData(new GridData(0, SWT.BEGINNING, false, false));
		
		removeButton.addMouseListener(new MouseListener() {
			public void mouseDoubleClick(MouseEvent e) {
			}

			public void mouseDown(MouseEvent e) {
			}

			public void mouseUp(MouseEvent e) {
				removeButtonListener();
			}
		});
		
		
		upProp = new Button(shell, SWT.PUSH);
		upProp.setImage(IMG_UP);
		upProp.setVisible(true);
		upProp.setLayoutData(new GridData(0, SWT.BEGINNING, false, false));
		
		upProp.addMouseListener(new MouseListener() {
			public void mouseDoubleClick(MouseEvent e) {
			}

			public void mouseDown(MouseEvent e) {
			}

			public void mouseUp(MouseEvent e) {
				upPropListener();
			}
		});

		
		downProp = new Button(shell, SWT.PUSH);
		downProp.setImage(IMG_DOWN);
		downProp.setVisible(true);
		downProp.setLayoutData(new GridData(0, SWT.BEGINNING, false, false));

		downProp.addMouseListener(new MouseListener() {
			public void mouseDoubleClick(MouseEvent e) {
			}

			public void mouseDown(MouseEvent e) {
			}

			public void mouseUp(MouseEvent e) {
				downPropListener();
			}
		});

    	return comp;
    }
     
	
	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.ui.dialogs.LabelEditorDialog#buttonPressed(int)
	 */
	/**
	 * 
	 * 
	 * @param buttonId 
	 */
	@Override
	protected void buttonPressed(int buttonId) {
        super.buttonPressed(IDialogConstants.OK_ID);
    }
	
	
	/**
	 * properties table listener handling.
	 */
	private void propertiesListener() {
		// refresh propValuesTable
		if(doubleClickListener!=null) {
			propValuesTable.removeListener(SWT.MouseDoubleClick, doubleClickListener);
		}
		refreshPropValues();
		
		// retrieve selected property
		EStructuralFeature prop = (EStructuralFeature)properties.getSelection()[0].getData();
		
		// display header in property values table
		if(prop.getUpperBound()==-1) {
			valuesColumn.setText(prop.getName()+" : "+prop.getEType().getName()+" ["+prop.getLowerBound()+"..*]");
		} else {
			valuesColumn.setText(prop.getName()+" : "+prop.getEType().getName()+" ["+prop.getLowerBound()+".."+prop.getUpperBound()+"]");
		}
		
		// prop is multivalued
		if(prop.isMany()) {
			
			Object propValue = compositeClass.eGet(prop);
			List propValues = (List)propValue;
			
			// Creation of initial values in case of lower multiplicty greater or equal to one
			// Default values are used to specify initial values
			if((prop.getDefaultValue()!=null) && propValues.isEmpty()) {
				ArrayList valuesToAdd = new ArrayList();
				for(int i=0; i<prop.getLowerBound(); i++){
					valuesToAdd.add(prop.getDefaultValue());
				}
				compositeClass.eSet(prop, valuesToAdd);
			}

			final List tempValues = new ArrayList();
			
			for(int i=0; i<propValues.size(); i++) {
				tempValues.add(i, propValues.get(i));
			}
			
			refreshPropValues();
				
			// Property value edition
			doubleClickListener = new Listener() {
				public void handleEvent (Event event) {
					multiValuedPropTableListener(tempValues);
				}
			};

			propValuesTable.addListener(SWT.MouseDoubleClick,  doubleClickListener);

		}

		// prop is not multivalued
		else {
			/* if the property has a mutliplicity of one or has a value */
			if((prop.getLowerBound() != 0) || !prop.equals(null)) {
				//TableItem propValueItem = new TableItem(propValuesTable, SWT.NONE);
				
				//propValueItem.setText(compositeClass.eGet(prop).toString());
				
				refreshPropValues();
				
				doubleClickListener = new Listener() {
					public void handleEvent (Event event) {
						propValuesTableListener();
					}
				};
				
				propValuesTable.addListener(SWT.MouseDoubleClick,  doubleClickListener);
			}
		}
	}

	
	/**
	 * Properties table refresh.
	 */
	private void refreshPropValues() {
		propValuesTable.removeAll();
		propValuesTable.setSize(properties.getSize());
		
		// retrieve selected property
		EAttribute prop = (EAttribute)properties.getSelection()[0].getData();
		
		/*if(doubleClickListener != null)
			propValuesTable.removeListener(SWT.MouseDoubleClick, doubleClickListener);*/
		
		Object val = compositeClass.eGet(prop);
		
		// multivalued property
		if(prop.getUpperBound() != 1) {
			final List currentValues = (List)val;

			for(int i=0; i<currentValues.size(); i++) {
				TableItem item = new TableItem(propValuesTable, SWT.NONE);
				item.setText(currentValues.get(i).toString());
				item.setData(currentValues.get(i));
			}
			//propValuesTable.addListener(SWT.MouseDoubleClick, doubleClickListener);
		}
		
		// monovalued propperty
		else {
			TableItem item = new TableItem(propValuesTable, SWT.NONE);
			item.setText(val.toString());
			item.setData(val);
		}
	}

	
	/**
	 * Monovalued property value table listener handling.
	 */
	private void propValuesTableListener() {
		// retrieve selected property
		EAttribute prop = (EAttribute)properties.getSelection()[0].getData();


		/** prop type is an Enumeration */ 
		if(prop.getEType() instanceof EEnum) {

			// combo dialog construction
			List literals = ((EEnum)prop.getEType()).getELiterals();
			String[] enumLiteralNames = new String[literals.size()];
			for(int i=0; i<literals.size(); i++) {
				enumLiteralNames[i]= ((EEnumLiteral)literals.get(i)).getLiteral();
			}
			ComboSelectionDialog valueDialog = new ComboSelectionDialog(getShell(), "New value:", enumLiteralNames, "");
			int val = valueDialog.open();
			
			if(val==Window.OK) {
				compositeClass.eSet(prop, ((EEnum)prop.getEType()).getEEnumLiteral((String)valueDialog.getValue()));
			}
		}
		
		else {
			InputDialog valueDialog = new InputDialog(getShell(), "Property value editing", "Value:", String.valueOf(compositeClass.eGet(prop)), null);
			int val = valueDialog.open();

			if(prop.getEType().getName().equals("EInt")) {
				if(val == org.eclipse.jface.window.Window.OK) {
					compositeClass.eSet(prop, Integer.valueOf(valueDialog.getValue()));
					valueDialog.close();
				}
			}

			if(prop.getEType().getName().equals("EBoolean")) {
				if(val == org.eclipse.jface.window.Window.OK) {
					compositeClass.eSet(prop, Boolean.valueOf(valueDialog.getValue()));
					valueDialog.close();
				}
			}

			if(prop.getEType().getName().equals("EString")) {
				if(val == org.eclipse.jface.window.Window.OK) {
					compositeClass.eSet(prop, valueDialog.getValue());
					valueDialog.close();
				}
			}
		}
		
		// refresh
		refreshPropValues();
	}
	
	
	/**
	 * Multivalued properties table listener handling.
	 * 
	 * @param tempValues represents current values of the property
	 */
	private void multiValuedPropTableListener(List tempValues) {
		// retrieve selected property
		EAttribute prop = (EAttribute)properties.getSelection()[0].getData();
		
		// selected value index
		Object currentPropValue = propValuesTable.getSelection()[0].getData();
		int index = propValuesTable.getSelectionIndex();


		/** prop type is an Enumeration */ 
		if(prop.getEType() instanceof EEnum) {
			// combo dialog construction
			List literals = ((EEnum)prop.getEType()).getELiterals();
			String[] enumLiteralNames = new String[literals.size()];
			for(int i=0; i<literals.size(); i++) {
				enumLiteralNames[i]= ((EEnumLiteral)literals.get(i)).getLiteral();
			}
			ComboSelectionDialog valueDialog = new ComboSelectionDialog(getShell(), "New value:", enumLiteralNames, "");
			int val = valueDialog.open();
			
			if(val==Window.OK) {
				tempValues.set(index, valueDialog.getValue());
				compositeClass.eSet(prop, tempValues);
			}
		}

		
		else {
			InputDialog valueDialog = new InputDialog(getShell(), "Property value editing", "Value:", String.valueOf(((EList)compositeClass.eGet(prop)).get(index)), null);
			int val = valueDialog.open();

			if(prop.getEType().getName().equals("EInt")) {
				if(val == org.eclipse.jface.window.Window.OK) {
					tempValues.set(index, Integer.valueOf(valueDialog.getValue()));
					compositeClass.eSet(prop, tempValues);
					valueDialog.close();
				}
			}

			if(prop.getEType().getName().equals("EBoolean")) {
				if(val == org.eclipse.jface.window.Window.OK) {
					tempValues.set(index, Boolean.valueOf(valueDialog.getValue()));
					compositeClass.eSet(prop, tempValues);
					valueDialog.close();
				}
			}

			if(prop.getEType().getName().equals("EString")) {
				if(val == org.eclipse.jface.window.Window.OK) {
					tempValues.set(index, valueDialog.getValue());
					compositeClass.eSet(prop, tempValues);
					valueDialog.close();
				}
			}
		}
		
		// refresh
		refreshPropValues();
	}
	
	
	/**
	 * add button listener handling.
	 */
	private void addButtonListener() {
		// retrieve selected property
		EAttribute prop = (EAttribute)properties.getSelection()[0].getData();
		
		// if lower multiplicity is equal to upper multiplicity
		if(prop.getLowerBound()==prop.getUpperBound()) {
			PapyrusMessageDialog.display("Multiplicity of this property is"+prop.getLowerBound()+".."+prop.getUpperBound()+"\nImpossible to add a new value.", null);
			return;
		}

		
		Object currentVal = compositeClass.eGet(prop);
		ArrayList tempValues = new ArrayList();
		
		if(((prop.getLowerBound()==0) && (prop.getUpperBound()==1))) {
			if(compositeClass.eGet(prop) != null) {
				tempValues.add(currentVal);
			}
		}
		
		else if(prop.getUpperBound()!=1){
			EList currentValues = (EList)currentVal;
		
			if(currentValues!=null){
				for(int i=0; i<currentValues.size(); i++){
					tempValues.add(currentValues.get(i));
				}
			}
		}
		
		if(prop.isMany() || (tempValues.size() < prop.getUpperBound())) {
			Object newValue=null;
			
			// enumeration
			if(prop.getEType() instanceof EEnum) {

				// combo dialog construction
				List literals = ((EEnum)prop.getEType()).getELiterals();
				String[] enumLiteralNames = new String[literals.size()];
				for(int i=0; i<literals.size(); i++) {
					enumLiteralNames[i]= ((EEnumLiteral)literals.get(i)).getLiteral();
				}
				
				ComboSelectionDialog valueDialog;
				//if(prop.getDefaultValue() != null)
					//valueDialog = new ComboSelectionDialog(getShell(), "New value:", enumLiteralNames, prop.getDefaultValueLiteral());
				//else
					valueDialog = new ComboSelectionDialog(getShell(), "New value:", enumLiteralNames, "");
				
				int val = valueDialog.open();
				
				if(val==Window.OK) {
					newValue = valueDialog.getValue();
					valueDialog.close();
				}
			}
			
			// ecore primitive type
			else if(prop.getEType().getName().equals("EInt") ||
					prop.getEType().getName().equals("EBoolean") ||
					prop.getEType().getName().equals("EString")) {
				InputDialog valueDialog = new InputDialog(getShell(), "Property value editing", "Value:", prop.getDefaultValueLiteral(), null);
				int val = valueDialog.open();
				
				if(prop.getEType().getName().equals("EInt")) {
					if(val==Window.OK) {
						newValue = Integer.valueOf(valueDialog.getValue());
						valueDialog.close();
					}
				}
				
				else if(prop.getEType().getName().equals("EBoolean")) {
					if(val==Window.OK) {
						newValue = Boolean.valueOf(valueDialog.getValue());
						valueDialog.close();
					}
				}
				
				else if(prop.getEType().getName().equals("EString")) {
					if(val==Window.OK) {
						newValue = valueDialog.getValue();
						valueDialog.close();
					}
				}
			}
			
			tempValues.add(newValue);
			
			// if property is multivalued
			if(prop.getUpperBound()!=1) {
				compositeClass.eSet(prop, tempValues);
			// otherwise
			} else {
				compositeClass.eSet(prop, tempValues.get(0));
			}
			
			// Add the added value to the list of values
			TableItem propValueItem = new TableItem(propValuesTable, SWT.NONE);
			
			//display the new value
			propValueItem.setText(newValue.toString());
		}
		
		// refresh
		refreshProperties();
	}
	
	
	/**
	 * remove button listener handling.
	 */
	private void removeButtonListener() {
		// retrieve selected property
		EAttribute prop = (EAttribute)properties.getSelection()[0].getData();

		// if lower multiplicity is equal to upper multiplicity
		if(prop.getLowerBound()==prop.getUpperBound()) {
			PapyrusMessageDialog.display("Lower multiplicity of this property is "+prop.getLowerBound()+"\nImpossible to remove a value.", null);
			return;
		}
		
		Object currentVal = compositeClass.eGet(prop);
		ArrayList tempValues = new ArrayList();
		
		if(((prop.getLowerBound()==0) && (prop.getUpperBound()==1))) {
			if(currentVal != null) {
				tempValues.add(currentVal);
			}
		}
		else if(prop.getUpperBound()!=1) {
			EList currentValues = (EList)currentVal;
			
			for(int i=0; i<currentValues.size(); i++){
				tempValues.add(currentValues.get(i));
			}
		}
		
		if((prop.getLowerBound() == 0) || (tempValues.size() > prop.getLowerBound())) {
			
			tempValues.remove(propValuesTable.getSelection()[0].getData());
			
			if(prop.isMany()) {
				compositeClass.eSet(prop, tempValues);
			} else {
				compositeClass.eSet(prop, null);
			}
			
			// remove the property value form the table
			propValuesTable.remove(propValuesTable.getSelectionIndex());
			
			//refresh
			refreshPropValues();
			refreshProperties();
		} else {
			PapyrusMessageDialog.display("Lower multiplicity of "+prop.getName()+" is "+prop.getLowerBound(), null);
		}
	}

	
	/**
	 * upProp button listener handling.
	 */
	private void upPropListener() {
		// retrieve selected property
		EAttribute prop = (EAttribute)properties.getSelection()[0].getData();
		
		List currentValues = (List)compositeClass.eGet(prop);
		ArrayList tempValues = new ArrayList();
		for(int i=0; i<currentValues.size(); i++){
			tempValues.add(currentValues.get(i));
		}
		
		int index = propValuesTable.getSelectionIndex();
		if(index!=0) {
			Object temp = propValuesTable.getSelection()[0].getData();
			tempValues.set(index, tempValues.get(index-1));
			tempValues.set(index-1, temp);
			compositeClass.eSet(prop, tempValues);

			// Refresh
			refreshPropValues();
			int propIndex = properties.getSelectionIndex();
			refreshProperties();
			properties.select(propIndex);
			propValuesTable.select(index-1);
		}				
	}

	
	/**
	 * downProp button listener handling.
	 */
	private void downPropListener() {
		// retrieve selected property
		EAttribute prop = (EAttribute)properties.getSelection()[0].getData();

		List currentValues = (List)compositeClass.eGet(prop);
		ArrayList tempValues = new ArrayList();
		for(int i=0; i<currentValues.size(); i++){
			tempValues.add(currentValues.get(i));
		}

		
		int index = propValuesTable.getSelectionIndex();
		if( index < tempValues.size()) {
			Object temp = propValuesTable.getSelection()[0].getData();
			tempValues.set(propValuesTable.getSelectionIndex(), tempValues.get(index+1));
			tempValues.set(index+1, temp);
			compositeClass.eSet(prop, tempValues);

			// Refresh
			refreshPropValues();
			int propIndex = properties.getSelectionIndex();
			refreshProperties();
			properties.select(propIndex);
			propValuesTable.select(index+1);
		}				
	}
	
	/**
	 * properties table refresh.
	 */
	private void refreshProperties() {
		int propIndex = properties.getSelectionIndex();

		properties.removeAll();
		
		EList props = compositeClass.eClass().getEAllStructuralFeatures();
		
		Iterator iter = props.iterator();
		
		while(iter.hasNext()){
			EStructuralFeature prop = (EStructuralFeature)iter.next();
			TableItem item = new TableItem(properties, SWT.NONE);

			if(prop.getUpperBound()==-1) {
				item.setText(prop.getName()
						+" ["+prop.getLowerBound()+"..*] = "
						+ compositeClass.eGet(prop));
			} else {
				item.setText(prop.getName()
					+" ["+prop.getLowerBound()+".."+prop.getUpperBound()+"] = "
					+ compositeClass.eGet(prop));
			}
			
			item.setData(prop);
						
			propListener = new SelectionListener() {
				public void widgetSelected(SelectionEvent e) {
					propertiesListener();
				}
				
				public void widgetDefaultSelected(SelectionEvent e){
				}
			};
			properties.addSelectionListener(propListener);
		}
		properties.select(propIndex);

	}
}
